package com.javasea.netty.har.server.netty;


import com.javasea.netty.har.common.cache.ChannelMap;
import com.javasea.netty.har.common.entity.Heart;
import com.javasea.netty.har.common.entity.NioClient;
import com.javasea.netty.har.common.entity.Result;
import com.javasea.netty.har.common.entity.print.PrintStateMsgConfirm;
import com.javasea.netty.har.common.entity.print.PrintStateMsgNotice;
import com.javasea.netty.har.common.entity.print.QrPrintState;
import com.javasea.netty.har.common.entity.proto.HeartProto;
import com.javasea.netty.har.common.entity.proto.NioClientProto;
import com.javasea.netty.har.common.entity.proto.PrintStateMsgProto;
import com.javasea.netty.har.common.entity.proto.ResultProto;
import com.javasea.netty.har.common.utils.Converter;
import com.javasea.netty.har.common.utils.Sender;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * @ClassName: PrinterServerHandler
 * @Author: longxiaonan
 * @Description:
 */
@Slf4j
public class PrinterServerHandler extends SimpleChannelInboundHandler {

    /**
     * 客户端返回的结果
     */
    private Result result;

    /**
     * 心跳丢失次数
     */
    private int counter = 0;

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) {
        try {
            Channel channel = ctx.channel();
            if (msg instanceof HeartProto.Heart) {
                Heart heart = Converter.protobuf2Pojo((HeartProto.Heart) msg);
                ChannelMap.setChannel(heart.getNioClientCode(), channel);
    //            channel.writeAndFlush(msg);
                return;
            }
            log.info("Client say : " + msg.toString());
            if (msg instanceof NioClientProto.NioClient) {
                NioClient nioClient = Converter.protobuf2Pojo((NioClientProto.NioClient) msg);
                String nioClientCode = nioClient.getNioClientCode();
                ChannelMap.setChannel(nioClientCode, channel);
//                ChannelMap.setChannelLock(nioClient.getNioClientCode(), new ReentrantLock());
                // 刚刚通道激活，还是为空的
//                String nioClientCode = ChannelMap.findNioClientCode(ctx.channel());
            }
            // 通过锁的方式获取到发送的结果
            if (msg instanceof ResultProto.Result) {
                Result result = Converter.protobuf2Pojo((ResultProto.Result) msg);
            }
            // 客户端异步通知的打印结果
            if (msg instanceof PrintStateMsgProto.PrintStateMsgNotice) {
                PrintStateMsgNotice printStateMsgNotice = Converter.protobuf2Pojo((PrintStateMsgProto.PrintStateMsgNotice) msg);
                log.debug("获取到打印结果数据为：[{}]", msg);
                handlePrintStateMsg(ctx, printStateMsgNotice);
            }
        } finally {
            ReferenceCountUtil.release(msg);
        }
    }

    private void handlePrintStateMsg(ChannelHandlerContext ctx, PrintStateMsgNotice re) {
        // 通知已经接受到
        PrintStateMsgConfirm msgConfirm = PrintStateMsgConfirm.builder()
                .batchId(re.getBatchId())
                .qrnumber(re.getQrnumber())
                .build();
        Sender.send(ctx, msgConfirm);
        // 压入批量更新队列
        QrPrintState qrPrintState = new QrPrintState()
                .setBatchId(re.getBatchId())
                // 2未打印
                .setPrintState(re.getPrintState())
                .setPrintMsg(re.getPrintMsg())
                .setQrnumber(re.getQrnumber());
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info("RemoteAddress : " + ctx.channel().remoteAddress().toString() + " active !");
        super.channelActive(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.info("通道不活跃");
        Channel channel = ctx.channel();
        String nioClientCode = ChannelMap.findNioClientCode(channel);
        log.info("获取到的clientCode为:" + nioClientCode);
        if(nioClientCode != null) {
            ChannelMap.removeChannel(nioClientCode);
            ChannelMap.removeChannelLock(nioClientCode);
        }
//        ChannelMap.removeCountDownLatchMap(channel);
        super.channelInactive(ctx);
    }

    /** 如果不关闭通道，那么在重启client的时候会报错:disconnection  -->ChannelHandlerContext(TransactionHandler#0, [id: 0xf8d839fd, L:/192.168.0.101:61774 !R:/192.168.0.101:9999])
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
            throws Exception {
        super.exceptionCaught(ctx, cause);
        log.info("通道发生异常！", ctx.channel(), cause);
        Channel channel = ctx.channel();
        if (channel.isActive()) {
            ctx.close();
        }
    }

//        @Override
//    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
//            throws Exception {
//        System.out.println("Server exceptionCaught");
//        cause.printStackTrace();
//        closeOnFlush(ctx.channel());
//    }
//    static void closeOnFlush(Channel ch){
//        if(ch.isActive())
//            ch.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
//    }

    public Result getResult() {
        return result;
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            if (event.state().equals(IdleState.READER_IDLE)) {
                // 空闲40s之后触发 (心跳包丢失)
                if (counter >= 3) {
                    // 连续丢失3个心跳包 (断开连接)
//                    ctx.channel().close().sync();
//                    log.error("已与"+ctx.channel().remoteAddress()+"断开连接");
                } else {
                    counter++;
                    log.debug(ctx.channel().remoteAddress() + "丢失了第 " + counter + " 个心跳包");
                }
            }
        }
    }

}
